#ifndef __CC2DV2_H_
#define __CC2DV2_H_

/**
 * Desc: cocos2d-xV2系列的接口
 * Auth: 张宇飞
 * Date: 2014-09-05    
 */

#include "cocos2d.h"
#include "csnode.h"
#include "utils.h"
#include "coord.h"

USING_NS_CC;

/*********************| 宏定义 |*********************/
#define US_NS_CSB	using namespace CSB;
#define NS_CSB_BEGIN	namespace CSB {
#define NS_CSB_END	}

NS_CSB_BEGIN

/*********************| 功能函数 |*********************/
/* 按照锚点0.5, 0.5计算出一个CCSize大小的多边形 */
extern void CCSize2CP(const CCSize& s, convex_polygon_t* out);
#define CCSIZE_2_CP(_s, _out) \
	CP_STACK_4(_out); \
	CSB::CCSize2CP(_s, &_out)

/* 转化一个CCRect到多边形结构体, relative */
extern void CCRect2CP(const CCRect& rect, convex_polygon_t* out);
#define CCRECT_2_CP(_rect, _out) \
	CP_STACK_4(_out); \
	CSB::CCRect2CP(_rect, &_out)

/* 子弹碰撞的回调 */
typedef int (CCObject::*hit_check_func)(const convex_polygon_t*);
#define HIT_CHECK_SELECTOR(_f_)	(CSB::hit_check_func)(&_f_)

/*********************| 功能类 |*********************/
class BindProto
{
public:
	enum {
		RUNNING           = 0x1,// 运行绑定节点
		UPDATE_PASSIVE    = 0x2,// 接受被绑定的node的改变
		RM_BULLET_ATONCE  = 0x4,// 立刻删除活着的子弹,并禁止子弹的生成
		OBJ_COLOR_BLEND   = 0x8, // obj can set color and alpha
		BULLET_COLOR_BLEND= 0x10, // bullet can set color and alpha
		FINAL		  = 0x20// 立刻删除所有的子弹,取消子弹节点的连接
	};
	struct bindele_t{
		int		flag;		// 控制位
		char		objdatana[16];	// 绑定的索引
		csnode_t*	pcs;		
//		void*		udata;		// 用户自定义数据
		pos_t		posOffset;	// TODO:位置修正
		int16_t		angleOffset;	// 角度修正
		int16_t		live;		// 生死
	};
	/*  绑定用的结构 */
	struct bind_t {
 		std::string 		fname;
		BindProto*		proto;
		std::list<bindele_t*>	eleList;
	};
#ifdef CSB_DEBUG
public:
	BindProto() : _debugBuDrawNode(NULL) {}
#endif
	virtual ~BindProto() {}
public:
	inline static bind_t*	newBind() {
		bind_t* r = new bind_t;
		r->proto = NULL;
		return r;
	}
	static bindele_t*	getEleN(bind_t* pb, size_t n);
public:
	/* 开始发射 */
	virtual void		onBegin(const char*, bindele_t*) = 0;
	/* 结束发射 */
	virtual void		onEnd(const char*, bindele_t*) = 0;
	/* 发射器刷新 */
	virtual void		onObjUpdate(const char*, bindele_t*) = 0;
	/* 子弹出生 */
	virtual void		onBulletBorn(const char*, bullet_t*, bindele_t*) = 0;
	/* 子弹死亡 */
	virtual void		onBulletDead(const char*, bullet_t*, bindele_t*) = 0;
	/* 子弹刷新 */
	virtual int		onBulletUpdate(const char*, bullet_t*, bindele_t*) = 0;
	virtual void		onAutoMachine(const char*, bindele_t*, pos_t*) = 0;
	/* 解除绑定 */
	virtual void		unbindMe(bind_t*, const int) = 0;
	virtual void		clearBulletsOnce(bind_t*) = 0;
#ifdef CSB_DEBUG
public:
	void			showBulletDebugCP(bool show);
	inline void		clearDebugNode() {
		if (_debugBuDrawNode) _debugBuDrawNode->clear();
	}
protected:
	cocos2d::CCDrawNode*	_debugBuDrawNode;
#endif
};

/* 子弹的发射协议 */
struct BulletProto
{
	/* 获取一个用于显示的子弹 */
	CCNode* (*emitBullet)(const int buID, const char* fname, const char* objdatana);
	/* 获取某种子弹的碰撞多边形的原始信息 */
	void (*orcp)(const int buID, const char *fname, const char *objdatana, convex_polygon_t *out);
};

typedef void (CCObject::*obj_end_func)(const BindProto::bindele_t* pe);
#define OBJ_END_SELECTOR(_f_)	(CSB::obj_end_func)(&_f_)

typedef void (CCObject::*auto_machine_func)(pos_t* out);
#define AUTO_MACHINE_SELECTOR(_f_)	(CSB::auto_machine_func)(&_f_)
/* 继承自BindProto , 做了基本的实现，添加了子弹的碰撞判定 */
class DefaultBindProto : public BindProto
{
public:
	DefaultBindProto(CCNode* bindNode, CCNode* buNode, const BulletProto& bp, CCObject* target = NULL, hit_check_func f = NULL,
			 CCObject* endTarget = NULL, obj_end_func fend = NULL)
		:_amTarget(NULL), _amCall(NULL),_bindNode(bindNode), _buNode(buNode), _buProto(bp),
		_hitTarget(target), _hitTest(f) ,_endTarget(endTarget), _endCall(fend){}
	virtual ~DefaultBindProto (){}
public:
	/* BindProto接口的实现 */
	virtual void		onBegin(const char*, bindele_t*);
	virtual void		onEnd(const char*, bindele_t*);
	virtual void		onObjUpdate(const char*, bindele_t*);
	virtual void		onBulletBorn(const char*, bullet_t*, bindele_t*);
	virtual void		onBulletDead(const char*, bullet_t*, bindele_t*);
	virtual int		onBulletUpdate(const char*, bullet_t*, bindele_t*);
	virtual void		onAutoMachine(const char*, bindele_t*, pos_t*);
	virtual void		unbindMe(bind_t*, const int);
	virtual void		clearBulletsOnce(bind_t*);
public:
	inline void		setHitCheckFunc(CCObject* target, hit_check_func f) {
		_hitTarget = target; _hitTest = f;
	}
	inline void		setEndCallFunc(CCObject* target, obj_end_func f) {
		_endTarget = target; _endCall = f;
	}
	inline void		setAutoMachine(CCObject* target, auto_machine_func f) {
		_amTarget = target; _amCall = f;
	}

	inline void		setBulletProto(const BulletProto& pb) {
		_buProto = pb;
	}
protected:
	void			updatePos(bindele_t*p);

protected:
	CCNode*			_bindNode;
	CCNode*			_buNode;
	BulletProto		_buProto;
	CCObject*		_hitTarget;
	hit_check_func		_hitTest;
	CCObject*		_endTarget;
	obj_end_func		_endCall;
	CCObject*		_amTarget;
	auto_machine_func	_amCall;
};

/* TODO: 按照时间线依次播放一个文件里面的组件 */
/* NOTE: 如何控制起始偏移的设定呢? */
//class TimeLineBindProto: public DefaultBindProto
//{
//};

#define OBJ_DATA_MAP_TYPE std::string, csobjdata_t*
class cc2dv2 : public CCNode
{
private:
	cc2dv2 ();
	~cc2dv2 ();
public:
	/* 系统初始化 */
	static bool 		init(const CCSize& winSize, cocos2d::CCScheduler* ps);
	/* 关闭系统 */
	static void 		end();
	/* 获取单例 */
	static cc2dv2*		instance();
	/* 获得数据对象的索引名 */
	static void 		CSMakeObjDataName(const int etype, const uint32_t NO, char* out, const size_t outlen);
	static void		setTargetFPS(unsigned int n);
public:
	/* 加载文件[列表]xxx.csb */
	bool			loadfile(const char* path);
	/* 如果有一个文件加载失败，就会导致加载  */
	bool			loadfile(const std::vector<const char*>& filesVec);
	/* 释放文件[列表], fname 是xxx.csb中的xxx */
	void			unloadobjdata(const char* fname, const char* objdatana);
	void			unloadfile(const char* fname);
	void			unloadAll();
	/* 绑定 */
	bool			bind(BindProto::bind_t* pb);
	/* 解除绑定 */
	void			unbind(BindProto::bind_t* pb, bool rmBu, bool dofinal = false);
	void			unbindAll(bool rmBu, bool dofinal = false);
	void			update(float dt);
	void 			run(BindProto::bind_t* p, bool r, bool emitNow = true);
	void 			run(BindProto::bind_t* p, size_t n, bool r, bool emitNow = true);
	// 咔咔掉所有的bullet
	void			kakaAllBulletsOnce();
	void			setScheduler(CCScheduler *scheduler);
private:
	/* 绑定用的map */
	std::map<std::string, std::map<OBJ_DATA_MAP_TYPE>* > _objsMap;
	/* 绑定了的节点, bool是控制是否刷新的 */
	std::set<BindProto::bind_t*>	_bindSet;
	float			_timeScale;
};

NS_CSB_END

#endif /* end of include guard: __CC2DV2_H_ */
